package com.gitee.cashzhang27.test.cloud.oauth.auth.server.redis.configuration;

import com.gitee.cashzhang27.test.cloud.oauth.auth.server.redis.entity.UserDetails;
import com.gitee.cashzhang27.test.cloud.oauth.auth.server.redis.service.AuthUserDetailsService;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.Base64Utils;

/**
 * 认证服务器配置
 *
 * @author Cash Zhang
 * @version v1.0
 * @since 2019/01/26 11:32
 */
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

  /**
   * 自定义UserDetailsService
   */
  private final AuthUserDetailsService authUserDetailsService;
  /**
   * 认证管理器
   */
  private final AuthenticationManager authenticationManager;
  /**
   * Redis连接工厂
   */
  private final RedisConnectionFactory redisConnectionFactory;

  private RedisTemplate<String, String> redisTemplate;

  private PasswordEncoder passwordEncoder;

  private DataSource dataSource;

  /**
   * 用来配置客户端详情服务（ClientDetailsService）， 客户端详情信息在这里进行初始化， 你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
   */
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.jdbc(dataSource);
  }

  /**
   * 用来配置令牌端点(Token Endpoint)的安全约束.
   */
  @Override
  public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer
        .tokenKeyAccess("permitAll()")
        .checkTokenAccess("permitAll()")
        .allowFormAuthenticationForClients();
  }

  /**
   * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
   */
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
        .authenticationManager(authenticationManager)
        .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
        .tokenStore(tokenStore())
        .userDetailsService(authUserDetailsService)
        .reuseRefreshTokens(false)
        .accessTokenConverter(accessTokenConverter());
  }

  @Bean
  public TokenStore tokenStore() {
    RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
    tokenStore.setPrefix("oauth:");
    return tokenStore;
  }

  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
      // 重写增强token方法,用于自定义一些token返回的信息
      @Override
      public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
          OAuth2Authentication authentication) {
        UserDetails userDetailsVO = (UserDetails) authentication.getUserAuthentication()
            .getPrincipal();
        // 自定义一些token属性
        final Map<String, Object> additionalInformation = new HashMap<>(6);
        additionalInformation
            .put("user_id", userDetailsVO.getId());

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
        return super.enhance(accessToken, authentication);
      }
    };

    PublicKey publicKey = getPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCS0JbFw-kzbQKIjz7FcPNx5HQOSuqM-_yfRlGRjj22AA4dXpCFppKS5MK6iOebtTvc310ROukMaqWKEFgZE2mTRT7N_Le4Ro2dOeKL_fbXdbwfwJEwih-7wssY9iS-ATA05UvQl13dZbLjtXV3Ijwn8QF4nonYc6cUNqzzFxUAswIDAQAB");
    PrivateKey privateKey = getPrivateKey(
        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJLQlsXD6TNtAoiPPsVw83HkdA5K6oz7_J9GUZGOPbYADh1ekIWmkpLkwrqI55u1O9zfXRE66QxqpYoQWBkTaZNFPs38t7hGjZ054ov99td1vB_AkTCKH7vCyxj2JL4BMDTlS9CXXd1lsuO1dXciPCfxAXieidhzpxQ2rPMXFQCzAgMBAAECgYAsfJU7Lt6SjSUX4SP5qvyxlbKPBCdnehG155Ze3zWW2RRt1NJBFVTTuwrAgCyCM5wElRA74NhuQUCRAdvYGVhDaJ3XMqPBu61xH7JZJla0J-faH_6DiONi5LGs4X5b13b62N6VCFYTMPwhb8MixocIir-YG-4oLU5Q-ZePKCJPvQJBAM6Jm-stuukl4WB6QBOtArQmBM1teRPq_7w4SaCDRR0MWMzvxZU6pO1N7IVlPGgtdWYCs_Vs6v2cDVovRtYYc-UCQQC1-YCEqHdV-kVLIkydh5R8Va5bMg7IFeN9to5mOZ9wKqmLUwBHCIJ4sxtMIhBblIobJjrisdAsOmX68ILOXwi3AkEAnqKhSHyiYGtA9VpQlww-1GGTNLnN0pef_1B4dLn-vrX6CsZrSxh1DvPYJAlC4X1w-349_NbAkzRmKQvA67ZyZQJAW1gXo_9wj67mE5XvAXAqH9NehxZ0hwk9vT_i8PthxgsUOgR68i0aWP4G6Mt8jIveW0xwaJS7G0hhInqSHodjtwJAWe_KmawQ2fwUcfw6woztqYNJpGKIO9gfwgFqdu6L3nE-96pyndLacnSZWOaT3AbF5_c3pUGAV_c3f-OfY8aFtg==");

    accessTokenConverter.setKeyPair(new KeyPair(publicKey, privateKey));
//    accessTokenConverter.setSigningKey("123");
    return accessTokenConverter;
  }

  private final static String RSA_ALGORITHM = "RSA";

  private RSAPublicKey getPublicKey(String publicKeyStr) {
    //通过X509编码的Key指令获得公钥对象
    KeyFactory keyFactory;
    try {
      keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
      X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
          Base64Utils.decodeFromUrlSafeString(publicKeyStr));
      return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    } catch (Exception e) {
      throw new RuntimeException("得到公钥->[" + publicKeyStr + "时遇到异常]", e);
    }
  }

  public RSAPrivateKey getPrivateKey(String privateKeyStr) {
    //通过PKCS#8编码的Key指令获得私钥对象
    KeyFactory keyFactory;
    try {
      keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
      PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
          Base64Utils.decodeFromUrlSafeString((privateKeyStr)));
      return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    } catch (Exception e) {
      throw new RuntimeException("得到私钥->[" + privateKeyStr + "时遇到异常]", e);
    }
  }

}
